package durak.client.control;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

import durak.client.view.MainDurakFrame;
import durak.common.control.GameCommand;
import durak.common.control.GameCommand.CommandType;
import durak.common.model.Card;
import durak.common.model.Cards;
import durak.common.model.GameTableState;
import durak.common.model.Hand;

public class ClientPlayerControl {

	private enum PlayerState {
		PL_ATTACKING, PL_DEFENDING, PL_WAITING
	};

	private String myName;
	private Hand hand;
	private GameTableState gameState;
	private ClientSessionControl clientSessionControl;
	MainDurakFrame mainFrame;
	PlayerState myCurrentState;
	private Cards possibleCardsToAttack;
	private Cards attackCards;
	private Card currentAttackCard; // for defend
	private Card[] currentDefendCards;
	private int defendFailures;
	private ICardListener handCardListener;
	private ICardListener tableCardListener;

	public ClientPlayerControl() {
		myName = new String("");
		hand = new Hand();
		mainFrame = new MainDurakFrame();
		possibleCardsToAttack = new Cards();
		currentDefendCards = new Card[6];
		myCurrentState = PlayerState.PL_WAITING;

		mainFrame.setPassAttackListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				passAttack();
			}
		});

		mainFrame.setWithdrawListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				withdraw();
			}
		});

		mainFrame.setAttackListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				attackPerformed();
			}
		});

		handCardListener = new ICardListener() {
			@Override
			public void cardClickedAction(Card card) {
				handCardClickedAction(card);
			}
		};

		tableCardListener = new ICardListener() {
			@Override
			public void cardClickedAction(Card card) {
				tableCardClickedAction(card);
			}
		};

		mainFrame.init();
		mainFrame.showMessageFromServer("Waiting for more potential duraks to join...");
		mainFrame.setVisible(true);
	}

	protected void attackPerformed() {
		if (!attackCards.isEmpty()) {
			clientSessionControl.sendResponseToAttack(attackCards);
			mainFrame.enablePassAttackButton(false);
			mainFrame.enableAttackButton(false);
		}
	}

	public void setClientSessionControl(ClientSessionControl serverSession) {
		this.clientSessionControl = serverSession;
	}

	public void handleReceivedMessageFromServer(ClientSessionControl clientSessionControl, GameCommand serverCommand) throws Exception {

		switch (serverCommand.getCommandType()) {
		case S2C_NOTIFY_STATE:
			responseToNotifyState(serverCommand);
			break;

		case S2C_ATTACK:
			responseToAttackCommand(serverCommand);
			break;

		case S2C_DEFEND:
			responseToDefendCommand(serverCommand);
			break;

		case S2C_GAME_FINISHED:
			responseToGameFinishedCommand(serverCommand);
			break;

		case S2C_ROUND_FINISHED:
			responseToRoundFinishedCommand(serverCommand);
			break;
		case S2C_TABLE_FULL:
			responseToTableFullCommand();
			break;
		default:

			break;
		}
	}

	private void responseToTableFullCommand() {
		clientSessionControl.sendResponsePassAttack();
		mainFrame.showMessageFromServer("You have to be quicker next time! The table is already full");
		mainFrame.enablePassAttackButton(false);
	}

	private void responseToNotifyState(GameCommand serverCommand) {
		if (myName.isEmpty()) {
			myName = serverCommand.getPlayerName();
		}
		hand.setCards(serverCommand.getCards().getCards());
		gameState = serverCommand.getGameState();
		mainFrame.showMessageFromServer("");
		updateTableView();
	}

	private void responseToRoundFinishedCommand(GameCommand serverCommand) {
	}

	private void responseToGameFinishedCommand(GameCommand serverCommand) {
		mainFrame.showMessageFromServer("Game Finished... Wait 5 seconds for new round to begin. Watch the scoreBoard for score updates");
		mainFrame.updateScoreBoard(serverCommand.getGameState());
	}

	private void responseToDefendCommand(GameCommand serverCommand) throws IOException {
		mainFrame.showMessageFromServer("You were attacked. Defend! Click on the card you are defending against first ");
		mainFrame.enableWithdrawButton(true);
		mainFrame.enablePassAttackButton(false);
		mainFrame.repaint();

		myCurrentState = PlayerState.PL_DEFENDING;
		defendFailures = 0;
		for (int i = 0; i < currentDefendCards.length; i++) {
			currentDefendCards[i] = null;
		}
	}

	private void responseToAttackCommand(GameCommand serverCommand) {
		attackCards = new Cards();

		mainFrame.showMessageFromServer("Do you wish to attack?");
		mainFrame.enablePassAttackButton(true);
		mainFrame.enableWithdrawButton(false);
		myCurrentState = PlayerState.PL_ATTACKING;
	}

	private void passAttack() {
		if (!gameState.getCardsOnTheTable().isEmpty()) {
			clientSessionControl.sendResponsePassAttack();
		} else {

			mainFrame.showMessageFromServer("Its the first attack card for the round, you must attack");
		}
		mainFrame.enablePassAttackButton(false);
	}

	private void withdraw() {
		mainFrame.enableWithdrawButton(false);
		clientSessionControl.sendResponseWithdraw();
	}

	private boolean MoreLegalCardstoAttack(Cards possibleCards, Cards attackCardsSoFar) {
		boolean isAble = false;

		if (gameState.getCardsOnTheTable() == null || gameState.getCardsOnTheTable().isEmpty()) {
			if (!attackCardsSoFar.isEmpty()) {
				for (Card attackCard : attackCardsSoFar) {
					for (Card cardFromHand : hand) {
						if (cardFromHand.getRank().compareTo(attackCard.getRank()) == 0 && !possibleCards.hasCard(cardFromHand)) {
							isAble = true;
							possibleCards.addCard(cardFromHand);
						}
					}
				}
			} else {
				possibleCards.addCards(hand);
				isAble = true;
			}
			return isAble;
		}

		for (Card cardOnTable : gameState.getCardsOnTheTable().getCards()) {
			for (Card cardFromHand : hand) {
				if (cardFromHand.getRank().compareTo(cardOnTable.getRank()) == 0 && !possibleCards.hasCard(cardFromHand)) {
					isAble = true;
					possibleCards.addCard(cardFromHand);
				}
			}
		}

		return isAble;
	}

	public boolean isAbleToDefend(Card defendCard, Card attackCard) {

		/**
		 * if the the card's suit is the same but it's rank is greater than the
		 * card he needs to beat. This also deals with if we have a kozar and
		 * the card is also a kozar
		 * */
		if (defendCard.getSuit().compareTo(attackCard.getSuit()) == 0 && defendCard.getRank().compareTo(attackCard.getRank()) >= 0)
			return true;
		/**
		 * if we have a kozar and the card to beat is not a kozar
		 */
		if (defendCard.getSuit().compareTo(gameState.getKozar().getSuit()) == 0 && attackCard.getSuit().compareTo(gameState.getKozar().getSuit()) != 0)
			return true;
		return false;
	}

	public void handCardClickedAction(Card card) {
		if (myCurrentState == PlayerState.PL_ATTACKING) {
			attackCardSelection(card);
		} else if (myCurrentState == PlayerState.PL_DEFENDING) {
			if (currentAttackCard == null) {
				mainFrame.showMessageFromServer("Please choose the card you are trying to defend from first");
				mainFrame.repaint();
			} else {
				defendCardSelection(card);
			}
		}
	}

	private void defendCardSelection(Card card) {
		if (isAbleToDefend(card, currentAttackCard)) {
			int indexOfAttackCard;

			mainFrame.showMessageFromServer("OK. You have succesfully defended with this card");
			hand.removeCard(card);
			indexOfAttackCard = gameState.getCardsOnTheTable().getAttackCards().indexOf(currentAttackCard);
			gameState.getCardsOnTheTable().addDefendCard(card, indexOfAttackCard);
			currentDefendCards[indexOfAttackCard] = card;
			defendFailures = 0;
			currentAttackCard = null;
			checkIfdoneDefending(gameState.getCardsOnTheTable().getDefendCards());
			updateTableView();
		} else {
			// need counter to three times
			defendFailures++;
			mainFrame.showMessageFromServer("You can't defend with this card, please choose another. " + Integer.toString(3 - defendFailures) + " tries left");
		}
		if (defendFailures > 3) {
			/* Failed to defend, breaking out */
			mainFrame.showMessageFromServer("You've exceeded the max number of retires to deffend yourself. You fail to defend!");
			mainFrame.repaint();
			GameCommand commandToServer = new GameCommand(CommandType.C2S_WITHDRAWN);
			clientSessionControl.sendC2SCommand(commandToServer);

			if (!gameState.getCardsOnTheTable().getDefendCards().isEmpty())
				hand.addCards(gameState.getCardsOnTheTable().getDefendCards());
		}
		updateTableView();
	}

	private void checkIfdoneDefending(Cards defendCards) {
		if (defendCards.size() == gameState.getCardsOnTheTable().getAttackCards().size()) {
			Cards thisSessionDefendCards = new Cards();
			for (int i = 0; i < currentDefendCards.length; i++) {
				Card defendCard = currentDefendCards[i];
				if (defendCard != null)
					thisSessionDefendCards.addCard(defendCard);
			}
			GameCommand commandToServer = new GameCommand(CommandType.C2S_DEFENDED, thisSessionDefendCards);
			clientSessionControl.sendC2SCommand(commandToServer);
		}
	}

	private void attackCardSelection(Card card) {
		MoreLegalCardstoAttack(possibleCardsToAttack, attackCards);
		if (possibleCardsToAttack.hasCard(card)) {
			attackCards.addCard(card);
			gameState.getCardsOnTheTable().addAttackCard(card);
			hand.removeCard(card);
			updateTableView();
		} else {
			mainFrame.showMessageFromServer("Illegal choise. Please choose again");
		}
		possibleCardsToAttack.removeAll();
		if (!attackCards.isEmpty()) {
			mainFrame.enableAttackButton(true);
		}
	}

	private void updateTableView() {
		mainFrame.getHandView().showHand(hand, handCardListener);
		mainFrame.getGamingTableView().showGamingTable(myName, gameState, tableCardListener, currentAttackCard);
		mainFrame.updateScoreBoard(gameState);
		mainFrame.repaint();
	}

	public void tableCardClickedAction(Card card) {
		if (myCurrentState == PlayerState.PL_DEFENDING) {
			defendAgainstCardSelection(card);
		}
	}

	private void defendAgainstCardSelection(Card card) {
		currentAttackCard = card;
		// highlight card
		updateTableView();
	}
}
